#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/3/27 14:10
# @Author  : liutianwei
# @File    : Auto_Change_Text.py
# @Software: PyCharm
import uuid
import requests
import json
import base64
import threading
import concurrent.futures
import fcntl
import os
import configparser
import subprocess
import re


class AutoChangeText:
    def __init__(self, caselist):
        self._wav_file_list = None
        self._silence_path = None
        self._caselist = caselist
        self._wav_filepath = ""
        self._appid = ""
        self._access_token = ""
        self._cluster = ""
        self._host = ""
        self._api_url = ""
        self._header = ""
        self._uid = ""
        self._voice_type_cmn = ""
        self._voice_type_eng = ""
        self._voice_type_chuan = ""
        self.parse_mongo_ini()
        return

    def parse_mongo_ini(self):
        data = []
        config = configparser.ConfigParser()
        # 获取当前路径
        config.read("conf/mongo.ini", encoding="utf-8")
        try:
            self._appid = config.get('tts', 'appid')
            self._access_token = config.get('tts', 'access_token')
            self._cluster = config.get('tts', 'cluster')
            self._api_url = config.get('tts', 'api_url')
            self._uid = config.get('tts', 'uid')
            self._voice_type_cmn = config.get('tts', 'voice_type_cmn')
            self._voice_type_eng = config.get('tts', 'voice_type_eng')
            self._voice_type_chuan = config.get('tts', 'voice_type_chuan')
            self._wav_filepath = config.get('tts', 'audio_path')
            self._header = {"Authorization": f"Bearer;{self._access_token}"}
        except Exception as e:
            print(f"Parsing the configuration file failed {e}")
        return True

    # 判断输入的字符串是中文还是英文
    def chose_language(self, audio_case):
        if re.search(r'[\u4e00-\u9fff]+', audio_case, re.I):
            return 'cmn'
        else:
            return 'eng'
    
    def set_language(self, audio_case):
        temp_list = audio_case.split(";")
        for input_type in temp_list:
            if re.search("ttsLang:", input_type):
                return input_type.split(':')[1]
        return self.chose_language(audio_case)

    def treat_case_fun(self, audio_case):
        temp_list = audio_case.split(";")
        for input_type in temp_list:
            if re.search("audioText:", input_type):
                return input_type.split(':')[1]
        return None

    def get_wav_resources(self, audio_case):
        file_name = [self.treat_case_fun(audio_case['INPUT']), self.set_language(audio_case['INPUT'])]
        if file_name[0] is None:
            print("Incoming parameter exception")
            return
        file_wav_name = file_name[0].replace(" ", "_").replace("'", "") + "_" + file_name[1] + ".wav"
        # 判断保存路径下是否存在相同名字的文件
        audio_path = os.path.join(self._wav_filepath, file_wav_name)
        if not os.path.exists(audio_path):
            # 获取二进制码流，转置生成语音文件
            # 设置语言选项
            voice_type = ''
            if file_name[1] == "cmn":
                voice_type = self._voice_type_cmn
            elif file_name[1] == "eng":
                voice_type = self._voice_type_eng
            elif file_name[1] == "chuan":
                voice_type = self._voice_type_chuan
            else:
                print("The input parameter is out of the value range,value is {}".format(file_name[1]))
            # 请求结构体
            request_json = {
                "app": {
                    "appid": self._appid,
                    "token": "access_token",
                    "cluster": self._cluster
                },
                "user": {
                    "uid": self._uid
                },
                "audio": {
                    "voice": "other",
                    "voice_type": voice_type,
                    "encoding": "wav",
                    "speed": 10,
                    "rate": 16000,
                    "volume": 10,
                    "pitch": 10
                },
                "request": {
                    "reqid": str(uuid.uuid4()),
                    "text": file_name[0],
                    "text_type": "plain",
                    "operation": "query",
                    "silence_duration": 1000
                }
            }
            try:
                resp = requests.post(self._api_url, json.dumps(request_json), headers=self._header)
            except Exception as e:
                raise Exception(f"Request failed {e}")
            if resp.status_code == 200:
                if "data" in resp.json():
                    data = resp.json()["data"]
                    file_wav_name_temp = file_name[0].replace(" ", "_") + "_" + file_name[1] + "_silence" + ".wav"
                    file_wav_path_temp = os.path.join(self._wav_filepath, file_wav_name_temp)
                    wav = open(file_wav_path_temp, "wb")
                    try:
                        fcntl.flock(wav.fileno(), fcntl.LOCK_EX)
                        wav.write(base64.b64decode(data))
                        wav.flush()
                    except FileNotFoundError as e:
                        print("Generate file error, reason: {}".format(e))
                        raise FileExistsError(f"Generate file error, reason: {e}")
                    finally:
                        # 对音频进行后处理
                        self.aftertreatment_silence(audio_path, file_wav_path_temp)
                        fcntl.flock(wav.fileno(), fcntl.LOCK_UN)
                        wav.close()
                else:
                    print("Error Obtaining voice binary stream. Error code is{}".format(resp.json()["code"]))
            else:
                print("request failed status: {}".format(resp.status_code))
                raise ConnectionError(f"request failed status: {resp.status_code}")
        # 对内存数据进行后处理替换成voice格式
        self.aftertreatment_case(audio_case, audio_path)

    # request获取audio，支持多线程生成
    def parse_pannel(self):
        # 创建静音片段
        if not os.path.exists(self._wav_filepath):
            os.makedirs(self._wav_filepath)
        self._silence_path = os.path.join(self._wav_filepath, "silence.wav")
        self._wav_file_list = os.listdir(self._wav_filepath)
        # 生1.0秒的静音文件，文件格式16bit 16000采集率
        if not os.path.exists(self._silence_path):
            os.system(f"sox -n -b 16 -r 16000 {self._silence_path} trim 0.0 1.0")

        # 设置线程数，最大线程为10
        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
            futures = []
            for case in self._caselist:
                futures.append(executor.submit(self.get_wav_resources, case))

            # 等待所有任务完成并打印结果  
            for future in concurrent.futures.as_completed(futures):
                try:
                    result = future.result()
                except Exception as e:
                    print(f"Task generated an exception: {e}")

    def aftertreatment_silence(self,wav_path,out_temp_voi):
        # 静音的长度，单位为毫秒
        # sox -n -r 48000 can-you-speak-chinese_eng.wav trim 0.0 0.500
        # a = os.system(f"sox {self._wav_temp} {out_temp_voi} {wav_path}")
        try:
            command = ['sox',self._silence_path,out_temp_voi,wav_path]
            status = subprocess.run(command,stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                    universal_newlines=True,
                                    check=True)
            if status.returncode == 0:
                print(f"The {wav_path} file is generated successfully")
                # 生成后删除临时文件
                os.remove(out_temp_voi)
                return True
            else:
                print(f"Error:The {wav_path} file is generated failed")
                return False
        except subprocess.CalledProcessError as e:
            print(f"Error: The {wav_path} file is generated failed,{e}")
            # raise Exception(f"Error: The {wav_path} file is generated failed,{e}")


    def aftertreatment_case(self, audio_case, audio_path):
        # 对case进行修改覆盖
        temp_list = audio_case['INPUT'].split(";")
        temp_list = ["voice:" + audio_path  if re.search("audioText", x) else x  for x in temp_list]
        temp_input=';'.join(temp_list)
        audio_case['INPUT'] = temp_input
        return audio_case


if __name__ == "__main__":

    """开发测试输入caselist"""

    test_case_list = {
        "file_path": f"/data1/liutianwei/mango/audio_tmp",
        "case_list": {
            ("今天是周几","cmn"),
            ("今天天气怎么样", "cmn"),
            ("北京的天气怎么样", "cmn"),
            ("北京的天气好吗", "cmn"),
            ("北京哪好玩", "cmn"),
            ("中午的饭有什么好吃的", "cmn"),
            ("今天是周二吗", "cmn"),
            ("can you speak english","eng"),
            ("can you speak chinese", "eng")
        },
    }

    auto_class = AutoChangeText(test_case_list)
    auto_class.parse_pannel()
